#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#ifdef _WIN32
#include <windows.h>
#include <windowsx.h>
#else
#include <sys/time.h>
#include <time.h>
#include "unistd.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include "math.h"
#include "string.h"

#include "GL/gl.h"
#include "GL/glu.h"
#include "SDL.h"
#include "SDL_ttf.h"

#include "BList.h"
#include "BVector.h"
#include "quaternion.h"
#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"
#include "keyboardstate.h"

#include "myglutaux.h"
#include "CMC.h"
#include "3dobject.h"
#include "AWLocalViewport.h"

#include "ode/ode.h"

#include "AWEntity.h"
#include "AWMessage.h"
#include "AWScenario.h"
#include "AWBody.h"
#include "AWSensor.h"
#include "AWBrain.h"
#include "AWAgent.h"
#include "AWWorld.h"

#include "GConsole.h"

#include "AwApp.h"
#include "loader.h"


/*						GLOBAL VARIABLES INITIALIZATION:							*/ 

long current_time=0,last_time=0;
float TIME_INC=0.01F,MAX_TIME_INC=0.1F;
float TIME_RATE=1.0F;

char *application_name="Agent World v0.5";
int application_version=0;
int SCREEN_DX=1024,SCREEN_DY=768;
int CONSOLE_HEIGHT1=256;
int CONSOLE_HEIGHT2=SCREEN_DY-32;
int COLOUR_DEPTH=32;
bool fullscreen=false;

/* Redrawing constant: */ 
int redraw_FPS=25;
int last_redraw,redraw_period=(redraw_FPS>0 ? 1000/redraw_FPS:0);

/* Keyboard update constant: */ 
int keyboard_FPS=25;
int last_keyboard,keyboard_period=(keyboard_FPS>0 ? 1000/keyboard_FPS:0);


/* Frames per second counter: */ 
int init_time=0;

// Application data: 
AW_World *World=0;
char *main_agent=0;
float world_time=0.0;
bool active=false;
int console_status=1;	// -1: unactive, 0: large, 1: small
bool quit = false;


/*						AUXILIAR FUNCTION DEFINITION:							*/ 




SDL_Surface *initialization(int flags) 
{
    const SDL_VideoInfo* info=0;
    int bpp=0;
	SDL_Surface *screen;

	AW_Entity::write_debug_report("Initializing SDL\n");

    if(SDL_Init(SDL_INIT_VIDEO)<0) {
		AW_Entity::write_debug_report("Video initialization failed: %s\n",SDL_GetError());
		return 0;
    } /* if */ 

	AW_Entity::write_debug_report("SDL initialized\n");

    info=SDL_GetVideoInfo();

    if(!info) {
		AW_Entity::write_debug_report("Video query failed: %s\n",SDL_GetError());
		return 0;
    } /* if */ 

	if (fullscreen) {
		bpp=COLOUR_DEPTH;
	} else {
	    bpp=info->vfmt->BitsPerPixel;
	} /* if */ 

	AW_Entity::write_debug_report("Setting OpenGL attributes\n");

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);

	AW_Entity::write_debug_report("OpenGL attributes set\n");

	AW_Entity::write_debug_report("Initializing video mode\n");

    flags=SDL_OPENGL|flags;
	screen=SDL_SetVideoMode(SCREEN_DX,SCREEN_DY,bpp,flags);
    if(screen==0) {
		AW_Entity::write_debug_report("Video mode set failed: %s\n",SDL_GetError());
		return 0;
    } /* if */ 

	AW_Entity::write_debug_report("Video mode initialized\n");

	SDL_WM_SetCaption(application_name, 0);

	SDL_EnableUNICODE(1);

	TTF_Init();

	return screen;
} /* initialization */ 


void redraw(void)
{
	float fAspect;

	/* Recalcular perspectiva: */ 
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glPolygonMode(GL_FRONT,GL_FILL);
	glEnable(GL_NORMALIZE);

	glViewport(0, 0, SCREEN_DX,SCREEN_DY);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	if (SCREEN_DY) fAspect = (GLfloat)SCREEN_DX/SCREEN_DY;
			  else fAspect = 1.0f;
		
	gluPerspective(45.0f, fAspect, 0.1F, 100);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	/* Dibujar: */ 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer

	if (World!=0) {
		World->beforedraw();
		World->draw();
		World->afterdraw();
	} /* if */ 	

	if (console_status==0) draw_console(16,SCREEN_DY-(16+CONSOLE_HEIGHT2),SCREEN_DX-32,CONSOLE_HEIGHT2);
	if (console_status==1) draw_console(16,SCREEN_DY-(16+CONSOLE_HEIGHT1),SCREEN_DX-32,CONSOLE_HEIGHT1);

	SDL_GL_SwapBuffers();

} /* redraw */ 



#ifdef _WIN32
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
{
#else
int main(int argc, char** argv)
{
#endif

	SDL_Surface *screen_sfc;
	KEYBOARDSTATE *k;

	int time;
	SDL_Event event;
	bool need_to_redraw=true;

	AW_Entity::set_debug_report();
	AW_Entity::write_debug_report("Application started\n");

	screen_sfc = initialization((fullscreen ? SDL_FULLSCREEN : 0));
	if (screen_sfc==0) return 0;

	{
		// Load the world: 
		FILE *fp=fopen(AW_INITIAL_WORLD,"r");
//		FILE *fp=fopen("../worlds/CBVis.wld","r");
		if (fp!=0) {
			if (World!=0) delete World;
			World=(AW_World *)ClassLoader(fp,0,0);
			world_time=World->GetActTime();
			MAX_TIME_INC=World->GetMaxTimeInc();
			TIME_RATE=World->GetTimeRate();
			fclose(fp);
			if (active) World->unpause();
		} /* if */ 
	}

	k=new KEYBOARDSTATE();

	if (fullscreen) {
		screen_sfc = SDL_SetVideoMode(SCREEN_DX, SCREEN_DY, COLOUR_DEPTH, SDL_OPENGL|(fullscreen ? SDL_FULLSCREEN : 0));
		SDL_WM_SetCaption(application_name, 0);
		SDL_ShowCursor(SDL_DISABLE);

		SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
	} /* if */ 

	time=init_time=SDL_GetTicks();

	while (!quit) {
		while( SDL_PollEvent( &event ) ) {
            switch( event.type ) {
                /* Keyboard event */
                case SDL_KEYDOWN:
					if (event.key.keysym.sym==SDLK_F12) {
						quit = true;
					} /* if */ 

#ifdef _WIN32
					if (event.key.keysym.sym==SDLK_F4) {
						SDLMod modifiers;

						modifiers=SDL_GetModState();

						if ((modifiers&KMOD_ALT)!=0) quit = true;
					} /* if */ 
#endif

					if (event.key.keysym.sym==SDLK_c) {
						SDLMod modifiers;

						modifiers=SDL_GetModState();

						if ((modifiers&KMOD_CTRL)!=0) {
							console_status=console_status+1;
							if (console_status>1) console_status=-1;
						} // if 
					} /* if */ 


					if (event.key.keysym.sym==SDLK_RETURN) {
						SDLMod modifiers;

						modifiers=SDL_GetModState();

						if ((modifiers&KMOD_ALT)!=0) {
							/* Toogle FULLSCREEN mode: */ 
							if (fullscreen) fullscreen=false;
									   else fullscreen=true;

							screen_sfc = SDL_SetVideoMode(SCREEN_DX, SCREEN_DY, COLOUR_DEPTH, SDL_OPENGL|(fullscreen ? SDL_FULLSCREEN : 0));
							SDL_WM_SetCaption(application_name, 0);

							SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
							SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
							SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
							SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
						} /* if */ 
					} /* if */ 

					k->key_down(event.key.keysym);
					if (console_status==-1) World->KeyboardEvent(event.key.keysym.unicode);

                    break;

				case SDL_KEYUP:
					k->key_up(event.key.keysym);
					break;

				case SDL_MOUSEBUTTONDOWN:					
					{
						char tmp[80];
						sprintf(tmp,"mouse_click %i %i",event.button.x,event.button.y);
						push_message(tmp,0);
					}
					World->MouseClick(event.button.x,event.button.y);
					break;

                /* SDL_QUIT event (window close) */
                case SDL_QUIT:
                    quit = true;
                    break;
            } /* switch */ 
        } /* while */ 

		current_time=SDL_GetTicks();
		if (active) {
			TIME_INC=(TIME_RATE*float(current_time-last_time))/1000.0F;
			if (TIME_INC>MAX_TIME_INC) TIME_INC=MAX_TIME_INC;
			if (TIME_INC!=0) {
				World->actualize(world_time,TIME_INC);
				if (main_agent!=0 && !World->FindAgent(main_agent)) active=false;
				world_time+=TIME_INC;
				last_time=current_time;
			} /* if */ 
		} else {
			last_time=current_time;
		} /* if */ 

		if (current_time>last_keyboard+keyboard_period) {
			last_keyboard=current_time;
			cycle_console(k);
			k->cycle();
		} /* if */ 

		if (current_time>last_redraw+redraw_period) {
			last_redraw=current_time;
			redraw();
		} /* if */ 

		// If the simulation is not running, then spare some cycles with the OS:
		if (!active) {
#ifdef WIN32
			Sleep(1);
#else
			usleep(1);
#endif
		} /* if */ 
	} /* while */ 


	delete k;
	k=0;

	clear_console();

	dCloseODE();
	delete World;
	World=0;
	AW_Entity::write_debug_report("Finalizing SDL\n");
	TTF_Quit();
	SDL_Quit();
	AW_Entity::write_debug_report("SDL finalized\n");
	Symbol::reset();
	AW_Entity::write_debug_report("Application finished\n");

	return 0;
} /* main */ 